package com.bio.jsf;

import com.bio.jpa.entities.*;
import com.bio.jpa.entities.File;
import java.io.*;
import com.bio.jsf.util.JsfUtil;
import com.bio.jpa.session.ProjectFacade;
import com.bio.tests.FastaParser;
import com.bio.tests.GenBankParser;

import java.util.*;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import javax.faces.event.ActionEvent;
import javax.faces.event.AjaxBehaviorEvent;
import java.math.BigInteger;
import java.util.logging.Level;
import org.apache.commons.io.FileUtils;
import org.primefaces.context.RequestContext;
import org.primefaces.event.DragDropEvent;
import org.primefaces.event.FileUploadEvent;
import org.primefaces.event.FlowEvent;
import org.primefaces.model.StreamedContent;
import org.primefaces.model.UploadedFile;

@ManagedBean(name = "projectController")
@SessionScoped
public class ProjectController implements Serializable {

    private Project current;
    private List<Project> items;
    private boolean skip;
    private ProjectHasUser map;
    private String aux;
    private String file_status = "Working";
    private String current_step;
    private User selectedUser;
    private List<GenBank> tmp_list_gb = new ArrayList();
    private List<Fasta> tmp_list_fasta = new ArrayList<Fasta>();
    private List<File> files = new ArrayList<File>();
    private List<java.io.File> tmpFiles = new ArrayList<java.io.File>();
    private List<User> myUsers = new ArrayList();
    private List<GenBank> myGenBanks = new ArrayList();
    private List<User> candidateUsers = new ArrayList();
    private static final Logger logger = Logger.getLogger(ProjectController.class.getName());
    @EJB
    private ProjectFacade ejbFacade;

    public List<GenBank> getMyGenBanks() {
        myGenBanks = getFacade().findMyGb(current.getPrjId());
        return myGenBanks;
    }

    public void setMyGenBanks(List<GenBank> myGenBanks) {
        this.myGenBanks = myGenBanks;
    }

    public List<java.io.File> getTmpfiles() {
        return tmpFiles;
    }

    public void setTmpfiles(List<java.io.File> tmpfiles) {
        tmpFiles = tmpfiles;
    }

    public String getFile_status() {
        return file_status;
    }

    public void setFile_status(String file_status) {
        this.file_status = file_status;
    }

    public User getSelectedUser() {
        return selectedUser;
    }

    public void setSelectedUser(User selectedUser) {
        this.selectedUser = selectedUser;
    }

    public void handleAnyFile(FileUploadEvent event) {
        System.out.println("Generic File Upload was requested " + event.getFile().getFileName());
        UploadedFile ufile = event.getFile();
        tmpFiles.add(FileController.saveToFile(ufile));
    }

    public String listUploadedFiles() {
        processFirstLines();

        return "ListUploadedFiles";
    }

    public void processFirstLines() {
        FileController fc = (FileController) FacesContext.getCurrentInstance().getApplication().getELResolver().
                getValue(FacesContext.getCurrentInstance().getELContext(), null, "fileController");

        GenBankParser genbank_parser = new GenBankParser();
        FastaParser fasta_parser = new FastaParser();
        tmp_list_gb.clear();
        tmp_list_fasta.clear();

        for (java.io.File f : tmpFiles) {
            if (fc.headOfFile(f, "LOCUS", 1)) {
                System.out.append("File is a genbank");
                tmp_list_gb.addAll(genbank_parser.parse(f));
            } else if (fc.headOfFile(f, ">", 1)) {
                System.out.append("File is a Fasta");
                tmp_list_fasta.addAll(fasta_parser.parse(f));
            }
        }
    }

    public List<GenBank> getTmp_list_gb() {
        return tmp_list_gb;
    }

    public void setTmp_list_gb(List<GenBank > tmp_list_gb) {
        this.tmp_list_gb = tmp_list_gb;
    }

    public List<Fasta> getTmp_list_fasta() {
        return tmp_list_fasta;
    }

    public void setTmp_list_fasta(List<Fasta> tmp_list_fasta) {
        this.tmp_list_fasta = tmp_list_fasta;
    }

    public void handleFileUpload(FileUploadEvent event) {
        System.out.println("File Upload was requested " + event.getFile().getFileName());

        FileController fc = (FileController) FacesContext.getCurrentInstance().getApplication().getELResolver().
                getValue(FacesContext.getCurrentInstance().getELContext(), null, "fileController");

        UploadedFile ufile = event.getFile();


        System.out.println("Original File size " + ufile.getSize());
        File dbfile = new File();
        if (ufile.getFileName().length() > 30) {
            dbfile.setFileName(ufile.getFileName().substring(0, 30) + ufile.getFileName().substring(ufile.getFileName().length() - 4, ufile.getFileName().length()));
            dbfile.setTitle(ufile.getFileName().substring(0, 30) + ufile.getFileName().substring(ufile.getFileName().length() - 4, ufile.getFileName().length()));
        } else {
            dbfile.setFileName(ufile.getFileName());
            dbfile.setTitle(ufile.getFileName());
        }
        dbfile.setFileSize(BigInteger.valueOf(ufile.getSize()));

        if (fc.headOfFile(ufile, "LOCUS", 30)) {
            dbfile.setFileType("GB File");
        } else {
            dbfile.setFileType(ufile.getContentType());
        }

        dbfile.setProjectPrjId(getCurrent());
        dbfile.setFileDate(new Date());

        fc.setCurrent(dbfile);
        dbfile.setFileDataList(fc.chunkFile(ufile));
        getFiles().add(dbfile);
        fc.create();


        synchronized (files) {
            getFiles().add(dbfile);
            FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO,
                    "The files were uploaded!", "");
            setFile_status("The file " + dbfile.getFileName() + " was saved");
            FacesContext.getCurrentInstance().addMessage(null, msg);

        }

    }

    public void handleFileUploadContigs(FileUploadEvent event) {
        System.out.println("File Upload was requested " + event.getFile().getFileName());
        UploadedFile ufile = event.getFile();
        java.io.File f = FileController.saveToFile(ufile);
        if (!tmpFiles.contains(f)) {
            tmpFiles.add(f);
        }
    }

    public void handleFileUploadFastas(FileUploadEvent event) {
        System.out.println("Fasta Upload was requested for " + event.getFile().getFileName());
        UploadedFile ufile = event.getFile();
        java.io.File f = FileController.saveToFile(ufile);

        FastaParser fp = (FastaParser) FacesContext.getCurrentInstance().getApplication().getELResolver().
                getValue(FacesContext.getCurrentInstance().getELContext(), null, "fastaParser");
        List<Fasta> fastas = null;
        fastas = fp.parse(f);
        tmp_list_fasta = fastas;

    }

    public String importFastas(List<Fasta> fastas, User owner) {
        FastaController controller = (FastaController) FacesContext.getCurrentInstance().getApplication().getELResolver().
                getValue(FacesContext.getCurrentInstance().getELContext(), null, "fastaController");

        for (Fasta fasta : fastas) {
            fasta.setCreationDate(new Date());
            fasta.setProject(current);
            fasta.setCreatedBy(owner);
            controller.setCurrent(fasta);
            controller.create();
        }
        tmp_list_fasta.clear();
        return prepareList();

    }

    public void importContigs(java.awt.event.ActionEvent act, User u) {
        RequestContext context = RequestContext.getCurrentInstance();
        System.out.println("Trying to import contigs for ");
        if (create() != null) {
            context.addCallbackParam("saved", true);
        }
    }

    public synchronized void getMessage() {
        RequestContext.getCurrentInstance().push("file_status", getFile_status());
    }

    public List<File> getFiles() {
        FileController controller = (FileController) FacesContext.getCurrentInstance().getApplication().getELResolver().
                getValue(FacesContext.getCurrentInstance().getELContext(), null, "fileController");
        files = controller.getFilesByProject(current.getPrjId());
        return files;
    }

    public void setFiles(List<File> files) {
        this.files = files;
    }

    public String getCurrent_step() {
        return current_step;
    }

    public void setCurrent_step(String current_step) {
        this.current_step = current_step;
    }

    public String getAux() {
        return aux;
    }

    public StreamedContent convertFile(File file) {
        FileController controller = (FileController) FacesContext.getCurrentInstance().getApplication().getELResolver().
                getValue(FacesContext.getCurrentInstance().getELContext(), null, "fileController");

        logger.info("File " + file.getTitle() + " is being converted " + file.getFileDataList());
        java.io.File f = controller.unChunckFile(file);
        System.out.println("File was unwrapped " + f.getAbsolutePath());
        return controller.convert(f);

    }

    public StreamedContent convertFile2(java.io.File file) {
        FileController controller = (FileController) FacesContext.getCurrentInstance().getApplication().getELResolver().
                getValue(FacesContext.getCurrentInstance().getELContext(), null, "fileController");
        logger.info("File " + file.getName() + " is being converted ");
        System.out.println(file.getAbsolutePath());
        return controller.convert(file);

    }
    private String last_file = "";

    public String getLast_file() {
        return last_file;
    }

    public void setLast_file(String last_file) {
        this.last_file = last_file;
    }

    public String loadFile(File file) {
        FileController controller = (FileController) FacesContext.getCurrentInstance().getApplication().getELResolver().
                getValue(FacesContext.getCurrentInstance().getELContext(), null, "fileController");
        ExternalContext extContext = FacesContext.getCurrentInstance().getExternalContext();

        logger.info("File " + file.getTitle() + " is being converted " + file.getFileDataList());
        java.io.File f = controller.unChunckFile(file);
        System.out.println(f.getAbsolutePath());
        FileInputStream input = null;
        try {
            input = new FileInputStream(f);
        } catch (FileNotFoundException ex) {
            Logger.getLogger(ProjectController.class.getName()).log(Level.SEVERE, null, ex);
        }
        last_file = f.getAbsolutePath();
        return last_file;
    }

    public void remFile(File file) {
        files.remove(file);
        FileController controller = (FileController) FacesContext.getCurrentInstance().getApplication().getELResolver().
                getValue(FacesContext.getCurrentInstance().getELContext(), null, "fileController");
        logger.log(Level.INFO, "Removing {0}", file.getFileName());

        // java.io.File result = new java.io.File(FileController.getUploadDirectory() + "/" + file.getFileName());
        // result.delete();


        controller.setCurrent(file);
        controller.destroyAndView();
        logger.log(Level.INFO, "File {0} deleted", file.getFileName());

        recreateModel();
        FacesMessage msg = new FacesMessage("Succesful deleted file ", file.getFileName());
        FacesContext.getCurrentInstance().addMessage(null, msg);
    }

    public synchronized void deleteFile(java.io.File f) {
        if (tmpFiles.contains(f)) {
            java.io.File file = tmpFiles.remove(tmpFiles.indexOf(f));
            FileUtils.deleteQuietly(file);
        } else {
            FileUtils.deleteQuietly(f);
        }


    }

    public synchronized void deleteFileFasta(Fasta f) {
        if (tmp_list_fasta.contains(f)) {
            tmp_list_fasta.remove(tmp_list_fasta.indexOf(f));
        }
    }

    public synchronized void deleteFiles() {
        tmpFiles.clear();
        tmp_list_fasta.clear();
        tmp_list_gb.clear();
    }

    public void setAux(String aux) {
        this.aux = aux;
    }

    public Project getCurrent() {
        if (current == null) {
            current = new Project();
        }
        return current;
    }

    public void setCurrent(Project project) {
        this.current = project;
    }

    public String addUser(User user) {
        myUsers.add(user);
        candidateUsers.remove(user);
        System.out.println("Adding User " + user.getUserLogin() + " " + user.getUserId() + " to Project " + current.getPrjName());
        if (user == null) {
            JsfUtil.addErrorMessage("User not selected");
        } else {
            map = new ProjectHasUser(new ProjectHasUserPK(current.getPrjId(), user.getUserId()));
            map.setUser(user);
            map.setProject(current);


            ProjectHasUserController controller = (ProjectHasUserController) FacesContext.getCurrentInstance().getApplication().getELResolver().
                    getValue(FacesContext.getCurrentInstance().getELContext(), null, "projectHasUserController");
            controller.setCurrent(map);
            controller.update();
            getItems().size();
        }
        return "View";
    }

    public void remUser(User user) {
        myUsers.remove(user);
        candidateUsers.add(user);
        System.out.println("Removing User [" + user.getUserLogin() + " " + user.getUserId() + "] to Project [" + current.getPrjDesc() + "]");
        map = new ProjectHasUser(new ProjectHasUserPK(current.getPrjId(), user.getUserId()));
        map.setProject(current);
        map.setUser(user);
        ProjectHasUserController controller = (ProjectHasUserController) FacesContext.getCurrentInstance().getApplication().getELResolver().
                getValue(FacesContext.getCurrentInstance().getELContext(), null, "projectHasUserController");
        controller.setCurrent(map);
        controller.destroy();
    }

    public void leaveFromProject(User user) {
        System.out.println(" User [" + user.getUserLogin() + " " + user.getUserId() + "] is leaving Project [" + current.getPrjDesc() + "]");
        map = new ProjectHasUser(new ProjectHasUserPK(current.getPrjId(), user.getUserId()));
        map.setProject(current);
        map.setUser(user);
        ProjectHasUserController controller = (ProjectHasUserController) FacesContext.getCurrentInstance().getApplication().getELResolver().
                getValue(FacesContext.getCurrentInstance().getELContext(), null, "projectHasUserController");
        controller.setCurrent(map);
        controller.destroy();
    }

    public List<Project> getItems() {
        items = null;
        items = getFacade().findAll();
        return items;
    }

    public void setItems(List<Project> items) {
        this.items = items;
    }

    public ProjectController() {
    }

    private ProjectFacade getFacade() {
        return ejbFacade;
    }

    public List<User> getCandidateUsers() {
        candidateUsers = getFacade().findUsersNotInProject(current.getPrjId());
        return candidateUsers;
    }

    public void setCandidateUsers(List<User> members) {
        this.candidateUsers = members;
    }

    public List<User> getMyUsers() {
        myUsers = getFacade().findMyUsers(current.getPrjId());
        return myUsers;
    }

    public void setMyUsers(List<User> myUsers) {
        this.myUsers = myUsers;
    }

    public String prepareList() {
        recreateModel();
        return "List";
    }

    public String prepareView() {
        myUsers = null;
        candidateUsers = null;
        files = null;
        return "View";
    }

    public void listener(AjaxBehaviorEvent event) {
        System.out.println("To validate user groups");
    }

    public void save(ActionEvent actionEvent) {
        FacesMessage msg = new FacesMessage("Successful", "Welcome :" + current.getPrjDesc());
        FacesContext.getCurrentInstance().addMessage(null, msg);
    }

    public boolean isSkip() {
        return skip;
    }

    public void onDrop(DragDropEvent ddEvent) {
        User user = ((User) ddEvent.getData());
        addUser(user);
    }

    public void setSkip(boolean skip) {
        this.skip = skip;
    }

    public String onFlowProcess(FlowEvent event) {
        logger.log(Level.INFO, "Current wizard step:{0}", event.getOldStep());
        logger.log(Level.INFO, "Next step:{0}", event.getNewStep());
        setCurrent_step(event.getOldStep());
        if (skip) {
            skip = false;   //reset in case user goes back
            return "confirm";
        } else {
            return event.getNewStep();
        }
    }

    public String prepareCreate() {
        logger.info("Users were get");
        files = new ArrayList<File>();
        current = new Project();
        return "/project/Create";
    }

    public String create() {
        try {
            current.setPrjCreation(new Date());
            getFacade().create(current);
            recreateModel();
            JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").getString("ProjectCreated"));
            return prepareView();
        } catch (Exception e) {
            e.printStackTrace();
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));
            return null;
        }
    }

    public String prepareEdit() {
        return "Edit";
    }

    public String update() {
        try {
            getFacade().edit(current);
            JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").getString("ProjectUpdated"));
            return "View";
        } catch (Exception e) {
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));
            return null;
        }
    }

    public String destroy() {
        performDestroy();
        recreateModel();
        return "List";
    }

    public void justDestroy() {
        performDestroy();
        recreateModel();
    }

    public void importGenBank(List<GenBank> gbfiles, User owner) {

        GenBankController controller = (GenBankController) FacesContext.getCurrentInstance().getApplication().getELResolver().
                getValue(FacesContext.getCurrentInstance().getELContext(), null, "genBankController");

        logger.log(Level.INFO, "File is being imported found {0}", gbfiles.size());
        for (GenBank genBank : gbfiles) {
            genBank.setCreatedBy(owner);
            genBank.setPrjFk(current);
            controller.setCurrent(genBank);
            controller.create();
        }
        JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").getString("GenBankCreated"));
        try {
            FacesContext.getCurrentInstance().getExternalContext().redirect("View");
        } catch (IOException ex) {
            Logger.getLogger(ProjectController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public synchronized void importContigs(List<java.io.File> files, User owner) {
        GenBankController controller = (GenBankController) FacesContext.getCurrentInstance().getApplication().getELResolver().
                getValue(FacesContext.getCurrentInstance().getELContext(), null, "genBankController");
        ContigController cg = (ContigController) FacesContext.getCurrentInstance().getApplication().getELResolver().
                getValue(FacesContext.getCurrentInstance().getELContext(), null, "contigController");
        GenBankParser gbp = new GenBankParser();
        synchronized (files) {
            Iterator<java.io.File> it = files.iterator();
            while (it.hasNext()) {
                java.io.File file = it.next();
                logger.log(Level.INFO, "Contig is being imported");
                Contig c = new Contig();
                for (GenBank genBank : gbp.parse(file)) {
                    c.setGenBankIdGen(genBank);
                    c.setCtgLength(genBank.getSequenceSize());
                    c.setDescription(genBank.getVersion());
                    genBank.setCreatedBy(owner);
                    genBank.setPrjFk(current);
                    genBank.setVersion(file.getName());
                    cg.setCurrent(c);
                    controller.setCurrent(genBank);
                    controller.create();
                    cg.create();
                    it.remove();
                    FileUtils.deleteQuietly(file);
                }
            }
        }
    }

    private void performDestroy() {
        try {
            UserController controller = (UserController) FacesContext.getCurrentInstance().getApplication().getELResolver().
                    getValue(FacesContext.getCurrentInstance().getELContext(), null, "userController");
            if (controller.getLoggedUser().getUserLogin().equalsIgnoreCase("ADMIN")) {
                getFacade().remove(current);
                JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").getString("ProjectDeleted") + " as ADMIN");
            } else if (controller.getLoggedUser().getUserId() == current.getCreatedBy().getUserId()) {
                getFacade().remove(current);
                JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").getString("ProjectDeleted"));
            } else {
                JsfUtil.addSuccessMessage("You are not authorized to delete this project");
            }
        } catch (Exception e) {
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));
        }
    }

    private void recreateModel() {
        items = null;
        files = null;
    }

    @FacesConverter(forClass = Project.class)
    public static class ProjectControllerConverter implements Converter {

        public Object getAsObject(FacesContext facesContext, UIComponent component, String value) {
            if (value == null || value.length() == 0) {
                return null;
            }
            ProjectController controller = (ProjectController) facesContext.getApplication().getELResolver().
                    getValue(facesContext.getELContext(), null, "projectController");
            return controller.ejbFacade.find(getKey(value));
        }

        java.lang.Integer getKey(String value) {
            java.lang.Integer key;
            key = Integer.valueOf(value);
            return key;
        }

        String getStringKey(java.lang.Integer value) {
            StringBuffer sb = new StringBuffer();
            sb.append(value);
            return sb.toString();
        }

        public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
            if (object == null) {
                return null;
            }
            if (object instanceof Project) {
                Project o = (Project) object;
                return getStringKey(o.getPrjId());
            } else {
                throw new IllegalArgumentException("object " + object + " is of type " + object.getClass().getName() + "; expected type: " + ProjectController.class.getName());
            }
        }
    }
}
